﻿using System;
using System.Collections;
using System.IO;
using System.Text;
using System.Xml;
using Metrics.Head;

namespace Metrics.Utils
{
    static class Xml
    {
        /// <summary>
        /// Узел в ревизии с определенным условием.
        /// </summary>
        public static XmlNode GetLogNode(int revision, string conditionOfPathNode)
        {
            return Getter.LogXmlDoc.SelectSingleNode(
                String.Format("//logentry[@revision = {0}]//path{1}",
                              revision, conditionOfPathNode));
        }

        /// <summary>
        /// Узлы в ревизии с определенным условием.
        /// </summary>
        public static XmlNodeList GetLogNodes(int revision, string conditionOfPathNode)
        {
            return Getter.LogXmlDoc.SelectNodes(
                String.Format("//logentry[@revision = {0}]//path{1}",
                              revision, conditionOfPathNode));
        }

        /// <summary>
        /// Узел ревизии в логе.
        /// </summary>
        public static XmlNode GetLogNode(int revision)
        {
            return Getter.LogXmlDoc.SelectSingleNode(
                String.Format("//logentry[@revision = {0}]", revision));
        }

        /// <summary>
        /// Ищет номер последней ревизии в Логе, в которой есть запись про файл.
        /// </summary>
        public static int FindLastNotation(int revNum, string filePath)
        {
            if (revNum == 0) return 0;

            //var node = GetLogNode(revNum, "text()=\"/" + filePath + '"');
            XmlNode node = null;
            foreach (XmlNode logNode in GetLogNodes(revNum, string.Empty))
            {
                if (!logNode.InnerText.Equals("/" + filePath)) continue;
                node = logNode;
                break;
            }
            return node == null ? FindLastNotation(revNum - 1, filePath) : revNum;
        }

        /// <summary>
        /// Ищет номер первой ревизии в Логе, в которой есть запись про файл.
        /// </summary>
        public static int FindFirstNotation(int revNum, string filePath)
        {
            var node = GetLogNode(revNum, "[text()=\"/" + filePath + "\"]");
            return node == null ? FindFirstNotation(revNum + 1, filePath) : revNum;
        }

        /// <summary>
        /// Возвращает номер последней ревизии на участке [1stNotation,lastNotation], для которой метрика посчитана.
        /// </summary>
        public static int FindLastRecord(int fileLastNotation, int fileFirstNotation, string filePath, string metricName)
        {
            var lastRecordRev = fileLastNotation;
            XmlNode metricNode = null;

            while (metricNode == null)
            {
                if (lastRecordRev < fileFirstNotation) return lastRecordRev;
                lastRecordRev = FindLastNotation(lastRecordRev, filePath);

                var revXmlFile = String.Format(@"{0}\{1}.xml", Program.MetricsPath, lastRecordRev);
                if (!File.Exists(revXmlFile))
                {
                    lastRecordRev--;
                    continue; // если дерево метрик не существует для данной ревизии
                }

                var revXmlDoc = new XmlDocument();
                try
                {
                    revXmlDoc.Load(revXmlFile);
                }
                catch (Exception)
                {
                    Console.WriteLine("Unable to open metric file {0}", revXmlFile);
                    Environment.Exit(1);
                }
                var xPathToFileNode = CreateStringXpathToFile(filePath);
                metricNode =
                    revXmlDoc.SelectSingleNode(String.Format("{0}/metric[@name=\"{1}\"]/text()", xPathToFileNode,
                                                             metricName));
                lastRecordRev--;

            }
            return ++lastRecordRev;
        }

        /// <summary>
        /// Строит строку-xPath до искомого узла.
        /// </summary>
        public static string CreateStringXpathToFile(string filePath)
        {
            var xString = new StringBuilder("/metricsTree");
            foreach (var name in filePath.Split('/'))
                xString.Append(String.Format("/path[@name=\"{0}\"]", name));
            return xString.ToString(); // на выходе путь изоморфный виду metrics/узел/.../узел-файл
        }

        /// <summary>
        /// Создает дочерний узел с именем, значением, атрибутами.
        /// Возвращает этот узел.
        /// </summary>
        public static XmlNode CreateNode(ref XmlNode parentNode, string name, object value, Hashtable attribute)
        {
            var xmlDoc = parentNode.OwnerDocument;
            var metricNode = xmlDoc.CreateNode(XmlNodeType.Element, name, null);
            if (attribute != null)
                foreach (var key in attribute.Keys)
                    metricNode.Attributes.Append(xmlDoc.CreateAttribute((string) key)).Value = (string) attribute[key];
            if (value != null) metricNode.InnerText = value.ToString();
            parentNode.AppendChild(metricNode);
            return metricNode;
        }
    }
}
